#ifndef __EVAL_H_
#define __EVAL_H_

const int Exchange[]= {
  0, 110, 330, 330, 550, 1990, 0,
  110, 330, 330, 550, 1990, 0
};

// Shamelessly copied some of these stuff from GnuChess. 
// 
const int
  /* CASTLE */
  cant_castle=-30,
  one_castle=-5,
  castled=10,

  /* PAWNS */
  pawns_eight=-10,
  pawn_backward=-12,
  pawn_doubled=-12,
  pawn_protected_pp=50,
  pawn_passed=10,
  pawn_near_king=40,		
  pawn_islands=-5,

  /* KNIGHTS */
  knight_rim=-13,
  knight_outpost=10,
  knight_pinned=-30,
  knight_trapped=-250,

  /* BISHOPS */
  bishop_double=18,
  bishop_outpost=8,
  bishop_pinned=-30,
  bishop_trapped=-250,
  bishop_fianchetto=8,

  /* ROOKS */
  rook_on_rank7= 30,
  rook_half_file=5,
  rook_open_file=6,
  rook_behind_pp=6,
  rook_infront_pp=-10,	
  rook_pinned=-50,
  rook_trapped=-10,

  /* QUEEN */
  queen_early=-40,
  queen_near_king=12,
  queen_pinned=-90,
  queen_trapped=0,
  queen_absent=-25,

  /* KING */
  king_open_file=-10,
  king_open_file1=-6,
  king_attack=10,
  king_defend=6,
  h_open=-600,
  g_open=-30,

  /* RR-QR-QQ Combo */
  combo_1 = 70,
  combo_2 = 30;


/// Position score for pawns.
const int pawn_sq[2][64]=
  {
    { 0,  0,  0,  0,   0,  0,  0,  0,
      5,  5,  5,-10, -10,  5,  5,  5,
     -2, -2, -2,  6,   6, -2, -2, -2,
      0,  0,  0, 25,  25,  0,  0,  0,
      2,  2, 12, 16,  16,  12, 2,  2,
      4,  8, 12, 16,  16,  12, 4,  4,
      4,  8, 12, 16,  16,  12, 4,  4,
      0,  0,  0,  0,   0,   0, 0,  0
    },
    {
      0,  0,  0,  0,   0,  0,  0,  0,
      4,  8, 12, 16,  16, 12,  4,  4,
      4,  8, 12, 16,  16, 12,  4,  4,
      2,  2, 12, 16,  16, 12,  2,  2,
      0,  0,  0, 25,  25,  0,  0,  0,
     -2, -2, -2,  6,   6, -2, -2, -2,
      5,  5,  5,-10, -10,  5,  5,  5,
      0,  0,  0,  0,   0,  0,  0,  0
    }
  };

/// Score for passed pawns on different ranks
const int Passed[2][8] =
  { {0,48, 48, 120, 144, 192, 240, 0}, {0, 240, 192, 144, 120, 48, 48, 0} };

/// Isolated pawns (1 or more) in some file.
const int isolated_normal[8] =
  { -8, -10, -12, -14, -14, -12, -10, -8 };

/// Isolated pawns with both files half-open(no enemy pawns).
const int isolated_weak[8] =
  { -22, -24, -26, -28, -28, -26, -24, -22 };


const int Outpost[2][64] =
  {
    {
      0, 0, 0, 0,  0, 0, 0, 0,
      0, 0, 0, 0,  0, 0, 0, 0,
      0, 0, 0, 0,  0, 0, 0, 0,
      0, 0, 1, 1,  1, 1, 0, 0,
      0, 1, 1, 1,  1, 1, 1, 0,
      0, 0, 1, 1,  1, 1, 0, 0,
      0, 0, 0, 1,  1, 0, 0, 0
    },
    {
      0, 0, 0, 0,  0, 0, 0, 0,
      0, 0, 0, 1,  1, 0, 0, 0,
      0, 0, 1, 1,  1, 1, 0, 0,
      0, 1, 1, 1,  1, 1, 1, 0,
      0, 0, 1, 1,  1, 0, 0, 0,
      0, 0, 0, 0,  0, 0, 0, 0,
      0, 0, 0, 0,  0, 0, 0, 0,
      0, 0, 0, 0,  0, 0, 0, 0
    }
  };

const int KingSq[64]=
  {
    24, 24, 24, 16, 16,  0, 32, 32, 	//Why 0 here?
    24, 20, 16, 12, 12, 16, 20, 24,
    16, 12,  8,  4,  4,  8, 12, 16,
    12,  8,  4,  0,  0,  4,  8, 12,
    12,  8,  4,  0,  0,  4,  8, 12,
    16, 12,  8,  4,  4,  8, 12, 16,
    24, 20, 16, 12, 12, 16, 20, 24,
    24, 24, 24, 16, 16,  0, 32, 32
  };

const int EndingKing[64]=
  {
     0,  6, 12, 18, 18, 12,  6,  0,
     6, 12, 18, 24, 24, 18, 12,  6,
    12, 18, 24, 32, 32, 24, 18, 12,
    18, 24, 32, 48, 48, 32, 24, 18,
    18, 24, 32, 48, 48, 32, 24, 18,
    12, 18, 24, 32, 32, 24, 18, 12,
     6, 12, 18, 24, 24, 18, 12,  6,
     0,  6, 12, 18, 18, 12,  6,  0
  };

int end_eval() {
  if(En.under_check())
    return -WIN;// LOSE
  else
    return 0;  //DRAW
}

/// No. of pawns in each file.
/**
 * WFile[i], contains no. of White Pawns in file i.
 * BFile[i], contains no. of Black Pawns in file i.
 *
 * Used for detecting
 *    1. open,half files,
 *    2. Isolated pawns.
 *    3. Doubled pawns.
 */

int WFile[n], BFile[n];
/// The position of least advanced pawn in each file.
/**
 * pWFile[i], position of least advanced White Pawn in file i.
 * pBFile[i], position of least advanced Black Pawn in file i.
 *
 * Default value n=8 for pWFile, indicates no white pawn in it.
 * Default value 0 for pBFile, indicates no black pawn in it.
 *
 * Used for detecting.
 *     1. Backward pawn.
 *     2. Passed pawn.  (& Protected)
 *     3. Outpost cells (& Controlled)
 *     4. Base pawn.
 */
int pWFile[n], pBFile[n];

/// most advanced passed pawn.
/**
 * =0 for WPFile indicates no passed pawn.
 * =n for BPFile indicates no passed pawn.
 * The most advanced passed pawn in file i is  WPFile[i]
 */
int WPFile[n], BPFile[n];
int Wscore,Bscore;
void pawn_structure() {
  for(int i=0;i<n;i++) {
    WFile[i]=BFile[i]=0;
    pWFile[i]=n;pBFile[i]=0;
  }
  Wscore=Bscore=0;

  for(int rank=0;rank!=n;rank++)
    for(int file=0;file!=n;file++) {
      piece p(En.P.CB[rank*n+file]);
      switch(p)
	{
	case EMPTY: break;
	case WPAWN:
	  WFile[file]++;
	  if(pWFile[file]==n)
	    pWFile[file]=rank;
	  break;
	case BPAWN:
	  BFile[file]++;
	  pBFile[file]=rank;
	  break;
	default: break;
	}
    }

  //
  // Computing the Score for pawn-islands.
  //
  /* WHITE */
  bool act=false;
  for(int i=0;i!=n;i++) {
    if(WFile[i]==0 and act)
      {
	Wscore+=pawn_islands;act=false;
      }
    else
	act=true;
  }
  if(act) Wscore+=pawn_islands;
  /* BLACK */
  act=false;
  for(int i=0;i!=n;i++) {
    if(BFile[i]==0 and act)
      {
	Bscore+=pawn_islands;act=false;
      }
    else
	act=true;
  }
  if(act)  Bscore+=pawn_islands;

  //
  // Computing the Score for isolated pawns.
  //
  /* WHITE */
  for(int i=1;i!=n-1;i++)
    if(WFile[i]!=0)
      if(WFile[i-1]==0 and WFile[i+1]==0)
	{
	  if(BFile[i-1]==0 and BFile[i+1]==0)
	    Wscore+=WFile[i]*isolated_weak[i];
	  else
	    Wscore+=WFile[i]*isolated_normal[i];
	}
  if(WFile[0]!=0 and WFile[1]==0) {
    if(BFile[1]==0)
      Wscore+=WFile[0]*isolated_weak[0];
    else
      Wscore+=WFile[0]*isolated_normal[0];
  }
  if(WFile[n-1]!=0 and WFile[n-2]==0) {
    if(BFile[n-2]==0)
      Wscore+=WFile[n-1]*isolated_weak[n-1];
    else
      Wscore+=WFile[n-1]*isolated_normal[n-1];
  }
  /* BLACK*/
  for(int i=1;i!=n-1;i++)
    if(BFile[i]!=0)
      if(BFile[i-1]==0 and BFile[i+1]==0)
	{
	  if(WFile[i-1]==0 and WFile[i+1]==0)
	    Bscore+=BFile[i]*isolated_weak[i];
	  else
	    Bscore+= BFile[i]*isolated_normal[i];
	}
  if(BFile[0]!=0 and BFile[1]==0) {
    if(WFile[1]==0)
      Bscore+=BFile[0]*isolated_weak[0];
    else
      Bscore+=BFile[0]*isolated_normal[0];
  }
  if(BFile[n-1]!=0 and BFile[n-2]==0) {
    if(BFile[n-2]==0)
      Bscore+=BFile[n-1]*isolated_weak[n-1];
    else
      Bscore+=BFile[n-1]*isolated_normal[n-1];
  }
  //
  // Computing the score for doubled pawns.
  //
  /* WHITE */
  for(int i=0;i!=n;i++)
    if(WFile[i]>1)
      Wscore+=pawn_doubled;
  /* BLACK */
  for(int i=0;i!=n;i++)
    if(BFile[i]>1)
      Bscore+=pawn_doubled;

  for(int i=0;i<n;i++) {
	  WPFile[i]=0;
	  BPFile[i]=n;
  }
  //
  // Computing the score for passed pawns.
  //
  /* WHITE */
  for(int i=1;i!=n-1;i++)
    if(WFile[i]!=0) {
      int min_rank=std::max(pBFile[i-1],pBFile[i+1]);
      for(int j=min_rank;j<n-1;j++)
	if(En.P.CB[j*n+i]==WPAWN)
	  {
	    WPFile[i]=std::max(WPFile[i],j);	
	    Wscore+=pawn_passed;
	    if(En.P.CB[(j-1)*n+i-1]==WPAWN or En.P.CB[(j-1)*n+i+1]==WPAWN)
	      Wscore+=pawn_protected_pp;
	    Wscore+=Passed[0][j];
	  }
    }
  //left-most file.
  if(WFile[0]==0) {
    int min=pBFile[1];
    for(int j=min;j<n-1;j++)
      if(En.P.CB[j*n]==WPAWN)
	{
	  WPFile[0]=std::max(WPFile[0],j);	
	  Wscore+=pawn_passed;
	  if(En.P.CB[(j-1)*n+1]==WPAWN)
	    Wscore+=pawn_protected_pp;
	  Wscore+=Passed[0][j];
	}
  }
  //right-most file.
  if(WFile[n-1]==0) {
    int min=pBFile[n-2];
    for(int j=min;j<n-1;j++)
      if(En.P.CB[j*n+n-1]==WPAWN)
	{
	  WPFile[n-1]=std::max(WPFile[n-1],j);	
	  Wscore+=pawn_passed;
	  if(En.P.CB[(j-1)*n+n]==WPAWN)
	    Wscore+=pawn_protected_pp;
	  Wscore+=Passed[0][j];
	}
  }


  /* BLACK */
  for(int i=1;i!=n-1;i++)
    if(BFile[i]!=0) {
      int min_rank=std::min(pWFile[i-1],pWFile[i+1]);
      for(int j=min_rank;j>0;j--)
	if(En.P.CB[j*n+i]==BPAWN)
	  {
	    BPFile[i]=std::min(BPFile[i],j);	
	    Bscore+=pawn_passed;
	    if(En.P.CB[(j+1)*n+i-1]==BPAWN or En.P.CB[(j+1)*n+i+1]==BPAWN)
	      Bscore+=pawn_protected_pp;
	    Bscore+=Passed[1][j];
	  }
    }
  //left-most file.
  if(BFile[0]==0) {
    int min=pWFile[1];
    for(int j=min;j>0;j--)
      if(En.P.CB[j*n]==BPAWN)
	{
	  BPFile[0]=std::min(BPFile[0],j);	
	  Bscore+=pawn_passed;
	  if(En.P.CB[(j+1)*n+1]==BPAWN)
	    Bscore+=pawn_protected_pp;
	  Bscore+=Passed[1][j];
	}
  }
  //right-most file.
  if(BFile[n-1]==0) {
    int min=pWFile[n-2];
    for(int j=min;j>0;j--)
      if(En.P.CB[j*n+n-1]==BPAWN)
	{
	  BPFile[n-1]=std::min(BPFile[n-1],j);	
	  Bscore+=pawn_passed;
	  if(En.P.CB[(j+1)*n+n]==BPAWN)
	    Bscore+=pawn_protected_pp;
	  Bscore+=Passed[1][j];
	}
  }
  //
  // Finding backward pawns that are not isolated, and score them.
  // Further we are interested only backward pawns in non-edge files.
  // Further for performance we assume there is only one pawn per file.
  //
  /* WHITE*/
  for(int i=1;i<n-1;i++)
    if(WFile[i]!=0 and (WFile[i+1]!=0 or WFile[i-1]!=0))
      if(pWFile[i]<pWFile[i+1] and pWFile[i]<pWFile[i-1])
	Wscore+=pawn_backward;
  /* BLACK*/
  for(int i=1;i<n-1;i++)
    if(BFile[i]!=0 and (BFile[i+1]!=0 or BFile[i-1]!=0))
      if(pBFile[i]>pBFile[i+1] and pBFile[i]>pBFile[i-1])
	Bscore+=pawn_backward;

}

int CWRank[8],CBRank[8];

bool end_game() {
  //FIXME	
  int counts[13]; // TODO make more portable.	

  for(int i=0;i<13;i++)
	  counts[i]=0;
  for(int i=0;i!=OUT;i++)
    {
      piece p(En.P.CB[i]);
      counts[p]++;
    }
  int wstrong=counts[WQUEEN]+counts[WROOK];
  int wweak  =counts[WBISHOP]+counts[WKNIGHT];
  int bstrong=counts[BQUEEN]+counts[BROOK];
  int bweak  =counts[BBISHOP]+counts[BKNIGHT];

  if(wstrong+wweak<=4 or bstrong+bweak<=4)
    return true;
  else
    return false;
  /*
  if(bstrong==0 and wstrong==0) {
	  if(wweak<=3 or bweak<=3)
		  return true;
  }
  if(wweak<=3 and bstrong>0)
	  return true;
  if(bweak<=3 and wstrong>0)
	  return true;
  return false; */
}
int eval_positions = 0;

int evaluate() {
  eval_positions++;

  int w_pawns=0,b_pawns=0;
  int w_queens=0,b_queens=0;
  int w_knights=0,b_knights=0;
  int w_bishops=0, b_bishops=0;
  int w_rooks=0, b_rooks=0;

  pawn_structure();

  if(En.P.A.castle_w)
    Wscore+=castled;
  else
    {
      if(not En.P.A.long_w or not En.P.A.short_w) {
	if(not En.P.A.long_w and not En.P.A.short_w)
	  Wscore+=cant_castle;
	else
	  Wscore+=one_castle;
      }
    }

  if(En.P.A.castle_b)
    Bscore+=castled;
  else
    {
      if(not En.P.A.long_b or not En.P.A.short_b) {
	if(not En.P.A.long_b and not En.P.A.short_b)
	  Bscore+=cant_castle;
	else
	  Bscore+=one_castle;
      }
    }

  for(int i=0;i!=OUT;i++)
    {
      piece p(En.P.CB[i]);
      if(colour_of(p)==WHITE)
	Wscore+=Exchange[p];
      else if(colour_of(p)==BLACK)
	Bscore+=Exchange[p];
      int f=FILE(i), r=RANK(i);

      switch(p) {
      case EMPTY: break;
      case WPAWN:
	w_pawns++;
	Wscore+=pawn_sq[0][i];
	if(std::abs(RANK(En.P.A.wk_pos)-r)<=1 and std::abs(FILE(En.P.A.wk_pos)-f)<=1)
	  Wscore+=pawn_near_king;	
	break;

      case WBISHOP:
	w_bishops++;
	if(Outpost[0][i]) {
	  bool op=true;
          if(f>0 and pBFile[f-1]>=r)
	    op=false;	
	  if(f<7 and pBFile[f+1]>=r)
	    op=false;
	  if(op)
		  Wscore+=bishop_outpost;
	}
/*	if(f!=0 and En.P.CB[i-1]==WBISHOP)
	  Wscore+=bishop_double;
	if(r!=0 and En.P.CB[i-n]==WBISHOP)
	  Wscore+=bishop_double;	*/
	if((i==B2 or i==G2))
	  Wscore+=bishop_fianchetto;     	
	if(En.pinned(i))
	  Wscore+=bishop_pinned;
        if(En.trapped(i))
	  Wscore+=bishop_trapped;	
	break;

      case WKNIGHT:
      {	
	w_knights++;
	if(Outpost[0][i]) {
	  bool op=true;
          if(f>0 and pBFile[f-1]>=r)
	    op=false;	
	  if(f<7 and pBFile[f+1]>=r)
	    op=false;
	  if(op)
	  Wscore+=knight_outpost;
        }
	if(f==0 or f==7 or r==0 or r==7)
	  Wscore+=knight_rim;	
	if(En.pinned(i))
	  Wscore+=knight_pinned;
        if(En.trapped(i))
	  Wscore+=knight_trapped;	
	
	break;
      }
      case WROOK:
	w_rooks++;
	CWRank[r]++;
	if(r==n-1)
	  Wscore+=rook_on_rank7;
	if(BFile[f]==0) {
	  if(WFile[f]==0)
	    Wscore+=rook_open_file;
	  else
	    Wscore+=rook_half_file;
	}
	if(WPFile[f]!=0) {
		if(r<WPFile[f])
			Wscore+=rook_behind_pp;
		else
			Wscore+=rook_infront_pp;
	}
	if(En.pinned(i))
	  Wscore+=rook_pinned;
        if(En.trapped(i))
	  Wscore+=rook_trapped;	
	break;

      case WQUEEN:
	w_queens++;
	CWRank[r]++;
	if(En.pinned(i))
	  Wscore+=rook_pinned;
        if(En.trapped(i))
	  Wscore+=rook_trapped;	
	if(std::abs(RANK(En.P.A.wk_pos)-r)<=1 and std::abs(FILE(En.P.A.wk_pos)-f)<=1)
	  Wscore+=queen_near_king;	
	break;

      case WKING:
      {
	if(WFile[f]==0 and BFile[f]==0) {
          if((f==7||f==0))
            Wscore+=h_open;
          else if((f==6||f==1))
            Wscore+=g_open;
	  else
	    Wscore+=king_open_file;	
	}
	break;
      }
      case BPAWN:
	b_pawns++;
	Bscore+=pawn_sq[1][i];
	if(std::abs(RANK(En.P.A.wk_pos)-r)<=1 and std::abs(FILE(En.P.A.wk_pos)-f)<=1)
	  Wscore+=pawn_near_king;	
	break;

      case BBISHOP:
	b_bishops++;
	if(Outpost[1][i]) {
	  bool op=true;
          if(f>0 and pWFile[f-1]>=r)
	    op=false;	
	  if(f<7 and pWFile[f+1]>=r)
	    op=false;
	  if(op)
	    Bscore+=bishop_outpost;
	}
/*	if(FILE(i)!=0 and En.P.CB[i-1]==BBISHOP)
	  Bscore+=bishop_double;
	if(RANK(i)!=0 and En.P.CB[i-n]==BBISHOP)
	  Bscore+=bishop_double;	*/
	if((i==B7 or i==G7))
	  Bscore+=bishop_fianchetto;     	
	if(En.pinned(i))
	  Bscore+=bishop_pinned;
        if(En.trapped(i))
	  Bscore+=bishop_trapped;	
	break;

      case BKNIGHT:
      {
	b_knights++;
	if(Outpost[1][i]) {
	  bool op=true;
          if(f>0 and pWFile[f-1]>=r)
	    op=false;	
	  if(f<7 and pWFile[f+1]>=r)
	    op=false;
	  if(op)
	  Bscore+=knight_outpost;
	}
	if(f==0 or f==7 or r==0 or r==7)
	  Bscore+=knight_rim;	
	if(En.pinned(i))
	  Bscore+=knight_pinned;
        if(En.trapped(i))
	  Bscore+=knight_trapped;	
	break;
      }
      case BROOK:
	b_rooks++;
	CBRank[r]++;
	if(r==0)
	  Bscore+=rook_on_rank7;
	if(WFile[f]==0) {
	  if(BFile[f]==0)
	    Bscore+=rook_open_file;
	  else
	    Bscore+=rook_half_file;
	}
	if(WPFile[f]!=0) {
		if(r<WPFile[f])
			Wscore+=rook_behind_pp;
		else
			Wscore+=rook_infront_pp;
	}
	if(En.pinned(i))
	  Bscore+=rook_pinned;
	if(En.trapped(i))
	  Bscore+=rook_trapped;	
	break;
      case BQUEEN:
	b_queens++;
	CBRank[r]++;
	if(En.pinned(i))
	  Bscore+=queen_pinned;
	if(En.trapped(i))
	  Bscore+=queen_trapped;	
	if(std::abs(RANK(En.P.A.wk_pos)-r)<=1 and std::abs(FILE(En.P.A.wk_pos)-f)<=1)
	  Wscore+=queen_near_king;	
	break;

      case BKING:
      {
	if(BFile[f]==0 and WFile[f]==0) {
          if((f==7||f==0))
            Bscore+=h_open;
          else if((f==6||f==1))
            Bscore+=g_open;
	  else
	    Bscore+=king_open_file;	
	}
	break;
      }
      }
    }

  if(En.no_of_moves()<10) {
    //TODO MAKE MORE SPECIFIC
    if(En.P.CB[D1]!=WQUEEN)
      Wscore+=queen_early;
    if(En.P.CB[D8]!=BQUEEN)
      Bscore+=queen_early;
  }
  //FIXME Correct Combos
  if(end_game()) {
    Wscore+=EndingKing[En.P.A.wk_pos];
    Bscore+=EndingKing[En.P.A.bk_pos];

    if(CBRank[n-1] and CBRank[n-2])
      Bscore+=combo_1;
    if(CWRank[0] and CWRank[1])
      Wscore+=combo_1;

  }else {
    Wscore+=KingSq[En.P.A.wk_pos];
    Bscore+=KingSq[En.P.A.bk_pos];

    if(CBRank[n-1] and CBRank[n-2])
      Bscore+=combo_2;
    if(CWRank[0] and CWRank[1])
      Wscore+=combo_2;
  }

  if(w_queens==0)
    Wscore+=queen_absent;
  if(b_queens==0)
    Bscore+=queen_absent;

  // XXX: Bad evaluation if all bishops are on same colour cell, this can never
  // happen for computer side if consistently played from beginning because it
  // only promotes to queen or to knight. This will be an issue if the side is
  // switched or when evaluating the position when opponent promoted to a bishop
  // of the same colour!
  bool mate_white=true, mate_black=true;
  if(w_pawns==0 and w_queens==0 and w_rooks==0) {
      if( w_bishops + w_knights <= 1)
	  mate_white=false;
  }

  if(b_pawns==0 and b_queens==0 and b_rooks==0) {
      if( b_bishops + b_knights <= 1)
	  mate_black=false;
  }
/*
  int score = (10000*(Wscore-Bscore))/(std::abs(Wscore)+std::abs(Bscore)+100);

  if(mate_white and not mate_black)
    score+=WIN-100;
  else if(not mate_white and mate_black)
    score+=WIN-100;
  else if(not mate_white and not mate_black)
    score=0;
*/
  int score;
  if(mate_white and not mate_black)
    score=WIN/2;
  else if(not mate_white and mate_black)
    score=-WIN/2;
  else if(not mate_white and not mate_black)
    score=0;
  else
    score = Wscore - Bscore;

  if(En.P.A.to_move==WHITE)
    return score;
  else
    return -score;
}

#endif
